package com.winit.dataInit;

import com.winit.Application;
import com.winit.common.base.BaseController;
import com.winit.common.base.ExecuteResult;
import com.winit.common.constants.IdTag;
import com.winit.common.constants.SystemTag;
import com.winit.commons.util.NumberUtil;
import com.winit.commons.util.SeqGenerate;
import com.winit.config.model.Config;
import com.winit.config.service.IConfigService;
import com.winit.feature.manager.FeatureManager;
import com.winit.feature.model.Feature;
import com.winit.feature.repository.FeatureOperations;
import com.winit.feature.repository.FeatureRepository;
import com.winit.feature.service.FeatureVersionGenerator;
import com.winit.feature.service.IFeatureService;
import com.winit.indexing.model.Indexing;
import com.winit.indexing.model.IndexingId;
import com.winit.indexing.service.IIdexingService;
import com.winit.menu.model.MatrixMenu;
import com.winit.menu.repository.MatrixMenuOperations;
import com.winit.menu.repository.MatrixMenuRepository;
import com.winit.menu.service.IMenuService;
import com.winit.page.model.Page;
import com.winit.page.repository.PageRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.*;
import java.util.List;


/**
 * Created by wangjian on 2017/10/27.
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class DateImporter {
    @Autowired
    FeatureOperations operations;
    @Autowired
    FeatureRepository featureRepository;
    @Autowired
    IConfigService configService;
    @Autowired
    MatrixMenuRepository menuRepository;
    @Autowired
    PageRepository pageRepository;
    @Autowired
    IIdexingService indexingService;
    @Autowired
    MatrixMenuOperations menuOperations;
    @Autowired
    private IFeatureService featureService;
    @Autowired
    FeatureManager featureManager;

    private static String tomPath = "/Users/wangjian/Desktop/tom.csv";
    private static String optPath = "/Users/wangjian/Desktop/opc.csv";

    /**
     * 导入时打开下方的@Test
     *
     * @throws Exception
     */
    @Test
    public void importFeature() throws Exception {
        importTom();
//        importOpt();
    }

    @Test
    public void importTom() throws Exception {
        MatrixMenu menuRoot = this.getBaseMenu("ROOT-TOM", "ROOT-TOM", "0", "TOM");
        menuRoot.setParent_id("0");
        this.importFromCsv(tomPath, SystemTag.TOM, menuRoot);
    }

    @Test
    public void importOpt() throws Exception {
        MatrixMenu menuRoot = this.getBaseMenu("ROOT-SELLERPORTAL", "ROOT-SELLERPORTAL", "0", "SELLERPORTAL");
        menuRoot.setParent_id("0");
        this.importFromCsv(optPath, SystemTag.OPT, menuRoot);
    }

    public MatrixMenu getBaseMenu(String code, String label, String level, String sys){
        MatrixMenu menu = new MatrixMenu();
        menu.setId(SeqGenerate.createId("MENU"));
        menu.setMenu_name(label);
        menu.setReq_id("-1");
        menu.setSys(sys);
        menu.setLevel(level);
        menu.setIs_del("1");
        menu.setStatus("ONLINE");
        Map<String, String> menu_data = new HashMap<>();
        menu_data.put("name", label);
        menu_data.put("routeId", " ");
        menu.setMenu_data(menu_data);
        menu.setMenuNum(code);

        return menu;
    }

    public Page getBasePage(String code, String label, String level, String sys){
        Page page = new Page();
        page.setId(SeqGenerate.createId("MP"));
        page.setFileName(code);
        page.setTitle(label);
        return page;
    }

    public Feature getBaseFeature(String code, String label, String level, String sys){
        Feature feature = new Feature();
        Map<String, String> attrs = new HashMap<>();
        attrs.put("feature_desc", "");
        attrs.put("feature_in", "");
        attrs.put("feature_logic", "");
        attrs.put("feature_name", label);
        attrs.put("feature_out", "");
        attrs.put("feature_role", "");

        feature.setAttr(attrs);
        feature.setId(SeqGenerate.createId("FE"));
        feature.setFeatureName(label);
        feature.setSys(sys);
        feature.setReqId("-1");
        feature.setStatus("ONLINE");
        return feature;
    }


    private void checkFileLegal(String filePath) throws Exception{
        boolean legal = true;
        List<String> lst = new ArrayList<>();
        System.out.println("****************duplicate code*****************");
        File file = new File(filePath);
        BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "GB2312"));
        String line = reader.readLine();
        while ((line = reader.readLine()) != null) {
            Map<String, Object> parser = this.parseLine(line);
            if (CollectionUtils.isEmpty(parser)) {
                continue;
            }
            String curCode = (String) parser.get("code");
            if (lst.contains(curCode)){
                legal = false;
                System.out.println(curCode);
            }else {
                lst.add(curCode);
            }
            if (true){
                continue;
            }
        }
        System.out.println("*********************************");
        reader.close();
        if (!legal){
            throw new Exception("duplicate code exists.");
        }
    }

    public void importFromCsv(String filePath, String sys, MatrixMenu root) throws Exception {
//        this.checkFileLegal(filePath);
//        if(true){
//            return;
//        }

        List<MatrixMenu> menus = new ArrayList<>();
        List<Page> pages = new ArrayList<>();
        List<Feature> features = new ArrayList<>();

        File file = new File(filePath);
        BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "GB2312"));

        List<Object> holder = new LinkedList<>();

        holder.add(root);
        MatrixMenu dbRoot = this.menuRepository.findByMenuNum(root.getMenuNum());
        if (dbRoot == null) {
            menus.add(root);
        }
        List<Integer> levelHolder = new LinkedList<>();
        levelHolder.add(0);
        String oldLine = null;
        //去掉首行
        String line = reader.readLine();
        while ((line = reader.readLine()) != null) {
            Map<String, Object> parser = this.parseLine(line);
            if (CollectionUtils.isEmpty(parser)){
                continue;
            }
            String curLabel = (String)parser.get("label");
            String curCode = (String)parser.get("code");
            int curLevel = (int)parser.get("level");
            Object curObject = null;
            int num = levelHolder.size();
            int holderLevel = levelHolder.get(num - 1);
            while (curLevel <= holderLevel) {
                holder.remove(holder.size() - 1);
                levelHolder.remove(levelHolder.size() - 1);
                holderLevel = levelHolder.get(levelHolder.size() - 1);
            }
            if (curLevel - holderLevel != 1){
                throw new Exception("数据错误，上下级之间跨度超过一级");
            }
            Object holderObject = holder.get(holder.size() - 1);
            if (curLevel <= 3){
                MatrixMenu holderMenu = (MatrixMenu)holderObject;
                MatrixMenu curMenu = this.getBaseMenu(curCode, curLabel, curLevel + "", sys);
                curObject = curMenu;
                curMenu.setParent_id(holderMenu.getId());
                holder.add(curObject);
                levelHolder.add(curLevel);

                MatrixMenu dbMenu = this.menuRepository.findByMenuNum(curCode);
                String parentCode = holderMenu.getMenuNum();
                MatrixMenu dbParent = this.menuRepository.findByMenuNum(parentCode);
                if (null != dbMenu){    //对应的menu_num在db中已经存在，目前不做任何操作
                    if (curLevel > 0 && dbParent == null){  //进行校验，其父级必定存在(追究一层)
                        throw new Exception("存在子节点，但父节点不存在，code:" + curCode);
                    }
                    continue;
                }

                /**--------------------------------------到了这里当前节点在db中不存在--------------------------------------*/
                if (dbParent != null){  //如果parent存在，则要更新Parent的children list
                    curMenu.setParent_id(dbParent.getId());
                    List<String> dbChildren = dbParent.getChildren();
                    dbChildren = dbChildren == null ? new ArrayList<String>() : dbChildren;
                    if (!dbChildren.contains(curMenu.getId())) {
                        MatrixMenu update = new MatrixMenu();
                        update.setId(dbParent.getId());
                        dbChildren.add(curMenu.getId());
                        update.setChildren(dbChildren);
                        this.menuOperations.updateByIdForPropertiesNotNull(update);
                    }
                }else {
                    List<String> children = holderMenu.getChildren();
                    if (null == children) {
                        children = new ArrayList<>();
                        holderMenu.setChildren(children);
                    }
                    children.add(curMenu.getId());
                }
                menus.add(curMenu);
            }

            if (curLevel == 4){
                MatrixMenu holderMenu = (MatrixMenu)holderObject;
                Page curPage = this.getBasePage(curCode, curLabel, curLevel + "", sys);
                curObject = curPage;
                curPage.setMenuId(holderMenu.getId());

                holder.add(curObject);
                levelHolder.add(curLevel);

                Page dbPage = this.pageRepository.findByFileName(curCode);
                String parentCode = holderMenu.getMenuNum();
                MatrixMenu dbParent = this.menuRepository.findByMenuNum(parentCode);
                if (dbPage != null){
                    if (dbParent == null){
                        throw new Exception("存在子节点，但父节点不存在，code:" + curCode);
                    }
                    continue;
                }
                if (dbParent != null){
                    curPage.setMenuId(dbParent.getId());
                }

                pages.add(curPage);
            }


            if (curLevel == 5){
                Page holderPage = (Page)holderObject;
                Feature curFeature = this.getBaseFeature(curCode, curLabel, curLevel + "", sys);
                curObject = curFeature;
                curFeature.setOriginalPageId(holderPage.getId());
                holder.add(curObject);
                levelHolder.add(curLevel);

                Feature dbFeature = this.featureRepository.findOne(curCode);
                String parentCode = holderPage.getFileName();
                Page dbParent = this.pageRepository.findByFileName(parentCode);
                if (dbFeature != null){
                    if (dbParent == null){
                        throw new Exception("存在子节点，但父节点不存在，code:" + curCode);
                    }
                    continue;
                }
                if (dbParent != null){
                    curFeature.setOriginalPageId(dbParent.getId());
                }

                features.add(curFeature);
            }


        }
            this.saveMenus(menus);
            this.savePages(pages);
            this.saveFeatures(features);
            reader.close();
    }



    private void saveMenus(List<MatrixMenu> menus){
        this.menuRepository.save(menus);
    }

    private void savePages(List<Page> pages){
        for (Page page : pages) {
            this.pageRepository.save(page);
            this.featureService.createPrototypeForPage(page);
        }
    }

    private void saveFeatures(List<Feature> features){

        for (Feature feature : features){
            feature.setType("COMMON");

            Map<String, String> attrs = feature.getAttr();
            attrs.put("date", System.currentTimeMillis() + "");
            this.updateVersionInfoForFeature(attrs, FeatureVersionGenerator.getVersionNo(), "", "");
            Config config = new Config();
            config.setKVMap(attrs);
            Config dbOne = this.configService.saveConfig(config);
            feature.setAttrConfigId(dbOne.getId());
            this.featureRepository.save(feature);

            String pageId = feature.getOriginalPageId();
            if (!StringUtils.isEmpty(pageId)) {
                featureManager.relateToPage("-1", feature.getId(), pageId);
            }
        }
    }

    private void updateVersionInfoForFeature(Map<String, String> configMap, String version, String masterVersion, String reqSnapshot){
        if (null != version) {
            configMap.put("version", version);
        }
        if (null != masterVersion) {
            configMap.put("master_version", masterVersion);
        }
        if (null != reqSnapshot) {
            configMap.put("req_snapshot", reqSnapshot);
        }
    }

    private Map<String, Object> parseLine(String line) throws Exception{
        if (StringUtils.isEmpty(line)){
            return null;
        }
        if (line.startsWith(",")){
            return null;
        }
        Map<String, Object> rs = new HashMap<>();
        line = line.replaceAll(" ", "");
        if (StringUtils.isEmpty(line.replaceAll(",", ""))){
            return null;
        }
        if(line.indexOf(",") <= 0){
            System.out.println("line is "+line);
            throw new Exception("错误的格式1-没有code");
        }
        String code = line.substring(0, line.indexOf(","));
        String content = line.substring(line.indexOf(","));
        String label = content.replaceAll(",", "");
        int level = content.indexOf(label);
        if (level < 0){
            System.out.println("line is "+line);
            throw new Exception("错误的格式2");
        }
        rs.put("code", code);
        rs.put("label", label);
        rs.put("level", level);
        return rs;
    }


    private void saveFeature(Feature feature) {
        feature.setStatus("ONLINE");
//        if ("0".equalsIgnoreCase(feature.getParentId())){
//            feature.setParentId(this.updateRoot(feature.getFeatureId()));
//        }
        feature.setReqId("-1");
        this.createConfig(feature);
//        List<Feature> children = feature.getFeatureChildren();
//        if (CollectionUtils.isEmpty(children)){
//            this.featureRepository.save(feature);
//            return;
//        }
        List<String> childs = new LinkedList<>();
//        feature.setChildren(childs);
//        for (Feature child : children){
//            childs.add(child.getFeatureId());
//            child.setParentId(feature.getFeatureId());
//            this.saveFeature(child);
//        }
        this.featureRepository.save(feature);
    }

    private String updateRoot(String childFeatureId) {
        Feature root = this.featureRepository.findByFeatureId("0");
        if (null == root) {
            root = new Feature();
            root.setId(SeqGenerate.createId(IdTag.FEATURE_ID));
//            root.setParentId("-1");
            root.setFeatureName("root");
//            root.setFeatureId("0");
//            root.setParentId("-1");
            root.setStatus("ONLINE");
            root.setFeatureName("root");
            List<String> children = new ArrayList<>();
            children.add(childFeatureId);
//            root.setChildren(children);
            this.saveFeature(root);
            return "0";
        }
//        List<String> children = root.getChildren();
//        if (null == children){
//            children = new ArrayList<>();
//        }
//        children.add(childFeatureId);
        Feature update = new Feature();
        update.setId(root.getId());
//        update.setChildren(children);
        this.operations.updatePropertiesNotNullById(update);
        return "0";
    }

    private Config createConfig(Feature feature) {
        Config config = new Config();
        Map<String, String> map = new HashMap<>();
//        map.put("feature_name", feature.getFeatureName());
        map.put("feature_role", "");
        map.put("feature_desc", "");
        map.put("feature_in", "");
        map.put("feature_out", "");
        map.put("feature_logic", "");
        map.put("feature_psc", "");
        map.put("feature_su", "");
        config.setKVMap(map);
        config = configService.saveConfig(config);
        feature.setAttrConfigId(config.getId());
        return config;
    }

    @Test
    public void getF() {
        Feature feature = this.featureRepository.findOne("FE2017111019365301383");
        System.out.println(feature);
    }
}
